(function () {
	// file drag hover
	function FileDragHover(e) {
		e.stopPropagation();
		e.preventDefault();
		var ndThis = angular.element(this);
		ndThis.removeClass("hover");
		if (e.type == "dragover") {
			ndThis.addClass("hover");
		}
	}

    function resolveConfig(scope, cfg) {
        var config = scope.$eval(cfg);

        if (!config) throw new Error("Directive need `upload-config` property");

        config.types = config.types || [/.*/];
        if (angular.isString(config.types)) {
            config.types = [config.types];
        }
        return config;
    }

	/**
	 * 上传文件并执行回调
	 * @param e
	 * @param cb {
	 * initCB: function(file, alias)
	 * progressCB: function(e, alias)
	 * finishCB: function(e, alias, xhr)
	 * }
	 * @constructor
	 */
	function FileSelectHandler(e, cb, config) {

		// cancel event and hover styling
		FileDragHover.bind(this)(e);

		// fetch FileList object
		var files = e.dataTransfer && e.dataTransfer.files;
		if (e.target.files && e.target.files.length) {
			files = e.target.files;
		}

		if (!files) {
			alert("Your browser seems do not support HTML5 upload...");
		}

        // 过滤
        var fs = [].filter.call(files, function(f){
            if (f.kind === "string") return false;
            return config.types.some(function(type){
                return type.test(f.type);
            });
        });

		processFiles(fs, cb, config);

	}

	function processFiles(files, cb, config) {
		// process all File objects
		for (var i = 0, f; f = files[i]; i++) {
			//使用一个自执行函数的原因是为了不让循环干扰回调函数的取值
			(function(){
				var alias = "s" + Math.floor(Math.random()*1000) + '-' + Math.floor(Math.random() * 1000) + '-' + (new Date() - 0);
				cb.initCB && cb.initCB(f, alias);
				UploadFile(f, function(e){/*progress callback*/
                    cb.progressCB && cb.progressCB(e, alias);
				}, function(e, xhr) { /* finish callback */
                    cb.finishCB && cb.finishCB(e, alias, xhr);
				}, config);
			})();
		}
	}
	/**
	 * 回调函数写法示例
	 * @param file
	 * @constructor
	 */
	function callbackExample(file) {

		// display an image
		if (file.type.indexOf("image") == 0) {
			var reader = new FileReader();
			reader.onload = function (e) {
				Output(
					"<p><strong>" + file.name + ":</strong><br />" +
						'<img src="' + e.target.result + '" /></p>'
				);
			}
			reader.readAsDataURL(file);
		}

		// display text
		if (file.type.indexOf("text") == 0) {
			var reader = new FileReader();
			reader.onload = function (e) {
				Output(
					"<p><strong>" + file.name + ":</strong></p><pre>" +
						e.target.result.replace(/</g, "&lt;").replace(/>/g, "&gt;") +
						"</pre>"
				);
			}
			reader.readAsText(file);
		}

	}


	// upload JPEG files
	function UploadFile(file, progressCB, finishCB, config) {

		// following line is not necessary: prevents running on SitePoint servers
		if (location.host.indexOf("sitepointstatic") >= 0) return

		var xhr = new XMLHttpRequest();
		if (xhr.upload) {
			// progress bar
			xhr.upload.addEventListener("progress", progressCB,  false);

			// file received/failed
			xhr.onreadystatechange = function (e) {
				if (xhr.readyState == 4) {
					finishCB(e, xhr);
				}
			};

			var fd = new FormData();
			fd.append('files', file);

			// start upload
            xhr.open("POST", config.url, true);

			xhr.setRequestHeader("X_FILENAME", file.name);
			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
			xhr.send(fd);

		}

	}

    /**
     *
     * @param e Native Event!
     * @param types Array of RegExp
     * @param cb function(file) {....}
     */
    function pasteHandler(e, types, cb) {
        // We need to check if event.clipboardData is supported (Chrome)
        if (e.clipboardData) {
            // Get the items from the clipboard
            var items = e.clipboardData.items;
            if (items) {
                // Loop through all items, looking for any kind of image
                for (var i = 0; i < items.length; i++) {
                    if (items[i].kind === "string") continue;
                    if (types.some(function(type){
                        return type.test(items[i].type);
                    })) {
                        // We need to represent the item as a file,
                        // if it cannot be getAsFile, null will be return
                        var blob = items[i].getAsFile();
                        blob && cb(blob);
                    }
                }
            }
        }
    }

	var uploadModule = angular.module("ui.ssnau.upload", []);
    /**
     * 用法：
     * <div upload-box="" upload-config="{url: urlBase + 'rest/upload/image, limit: 5'}"
     * config的参数：
     * {
     *  url: String 需要上传的目标绝对地址
     *  types:Array 类型['image', 'jpeg'], 匹配时将是(new RegExp('image')).test(targetType), 如果为空，将匹配所有类型  //TODO.
     *  limit: Integer 表示最大上传限制
     *  success: 成功时的回调
     *  fail: 失败时的回调
     *  process: 正在上传时的回调
     *  }
     */
    uploadModule.directive("uploadBox", ["urlBase", function (urlBase) {

			return {
				require: '?ngModel',
				scope: {
					cpImage: "=clipboard", //Blob类型：{type: "image/jpeg", size: integer, ...}
					imageArray: "=ngModel"//让isolate scope内部可以引用到外部ngModel的对象
				},
				template: '<div class="upload-area">' +
					'<div class="preview-block" ng-repeat="image in imageArray">' +
					'<img ng-src="{{image.thumbnail}}" />' +
					'<span ng-show="image.isUploading">{{image.percent}}&nbsp;</span>' +
					'<div ng-show="image.isUploading" class="dark-curtain"></div>' +
					'</div>' +
					'<div class="upload-block drop-area"><i class="icon-plus"></i> Drop Files Here or Choose File <input type="file" class="upload-choose-file" /></div>' +
					'</div>',

				link: function (scope, element, attrs, ctrl) {
					var elUploadBlock = element.find(".drop-area")[0],
                        config = resolveConfig(attrs.uploadConfig);
					/**
					 * 用于根据alias取得相应image
					 * @param alias
					 * @returns {*}
					 */
					function getImg(alias) {
						return scope.imageArray.filter(function (img) {
							return img.alias === alias;
						})[0];
					}

                    config.url = config.url ||urlBase + "rest/upload";
					var uploadCallbackHandler = {
						initCB: function (file, alias) {
							// display an image
							if (file.type.indexOf("image") == 0) {
								var reader = new FileReader();
								reader.onload = function (e) {
									scope.imageArray.push({
										thumbnail: e.target.result,
										alias: alias,
										isUploading: true,
										failure: false,
										percent: 0
									});
									scope.$apply();
								}
								reader.readAsDataURL(file);
							}
						},
						progressCB: function (e, alias) {
							var image = getImg(alias);
							image.percent = " 正在上传 ";
							scope.$apply();
						},
						finishCB: function (e, alias, xhr) {
							var image = getImg(alias);

							if (xhr.status == 200) {
								image.isUploading = false;
								image.id = xhr.responseText - 0;
                                config.success && config.success(xhr.responseText);
							} else {
								image.failure = true;
                                config.fail && config.fail(xhr.responseText);
							}
							scope.$apply();
						}
					};

					scope.$watch("cpImage", function(file) {
						if (file && file.type && file.size) {
							processFiles([file], uploadCallbackHandler, config);
						}
						scope.cpImage = null;
					});

                    //如果有限制图片上传数量，则监视当前imageArray,如果大于等于limit值，将上传框的display设成none
                    if (config.limit) {
                        scope.$watch("imageArray.length", function(length){
                            if (length >= config.limit) {
                                element.find(".upload-block").hide();
                            } else {
                                element.find(".upload-block").show();
                            }
                        });
                    }

					elUploadBlock.addEventListener("dragover", FileDragHover, false);
					elUploadBlock.addEventListener("dragleave", FileDragHover, false);
					elUploadBlock.addEventListener("drop", function (e) {
							FileSelectHandler.bind(this)(e, uploadCallbackHandler, config);
						}, false);

					element.on("change", ".upload-choose-file", function(e) {
						var ndThis = $(this),
								ele = ndThis[0];

						FileSelectHandler.bind(ele)(e, uploadCallbackHandler, config);
						angular.element(ele.outerHTML).insertAfter(ndThis);
						ndThis.remove();
					});

					angular.extend(scope, {
						imageArray: scope.imageArray || []
					});

				}
			};
		}]);


    uploadModule.directive("uploadPaste", function () {
        return function(scope, elem, attrs) {
            var config = resolveConfig(scope, attrs.uploadConfig);

            elem.on("paste", function(e){
                e = e.originalEvent.clipboardData ? e.originalEvent : e;
                pasteHandler(e, config.types, function(file){
                    UploadFile(file, config.process, function(e, xhr){
                        if (xhr.status == 200) {
                            config.success && config.success(xhr.responseText);
                        } else {
                            config.fail && config.fail(e);
                        }
                    }, config)
                });
            });
        }
    });
    uploadModule.directive("uploadInput", function () {
        return function(scope, elem, attrs) {
            var config = resolveConfig(scope, attrs.uploadConfig);

            elem.on("change", function(e) {
                var ndThis = $(this),
                    ele = ndThis[0];

                FileSelectHandler.bind(ele)(e, {
                    finishCB: function(e, alias, xhr) {
                        if (xhr.status == 200) {
                            config.success && config.success(xhr.responseText);
                        } else {
                            config.fail && config.fail(e);
                        }
                    }
                }, config);
            });
        }
    });
    uploadModule.directive("uploadDrag", function() {
        return function(scope, elem, attrs) {
        var config = resolveConfig(scope, attrs.uploadConfig),
            el = elem[0];

        el.addEventListener("dragover", FileDragHover, false);
        el.addEventListener("dragleave", FileDragHover, false);
        el.addEventListener("drop", function (e) {
            FileSelectHandler.bind(this)(e, {
                finishCB: function(e, alias, xhr) {
                    if (xhr.status == 200) {
                        config.success && config.success(xhr.responseText);
                    } else {
                        config.fail && config.fail(e);
                    }
                }
            }, config);
        }, false);
        }
    });

    //you MUST use constant("urlBase", 'your site address') to override!
    uploadModule.value("urlBase", "/");
})();